import hashlib
import logging
import re

from django.utils import timezone
from django.utils.timezone import localtime

from info.models import Commit, File, Repository

import git
import codecs

_TEXT_BOMS = (
    codecs.BOM_UTF16_BE,
    codecs.BOM_UTF16_LE,
    codecs.BOM_UTF32_BE,
    codecs.BOM_UTF32_LE,
    codecs.BOM_UTF8,
    )

CLONE_PREFIX = "./repos/"

def is_binary_file(file):
    initial_bytes = file.data_stream.read(8192)
    return not any(initial_bytes.startswith(bom) for bom in _TEXT_BOMS) and b'\0' in initial_bytes

def get_name_and_platform(repository_url):
    result = re.search(r'^https://([-\w]+\.)+\w+/([-\w/]*)?.git$', repository_url)
    if result is None:
        return None
    name = result.group(2)
    platform = result.group(1)[:-1]
    if name is None or platform is None:
        return None
    md5 = hashlib.md5()
    md5.update((platform + ":" + name).encode('utf-8'))
    hex_value = md5.hexdigest()
    return platform, name, hex_value

def binary_filter(commit, path):
    try:
        file = commit.tree[path]
        if is_binary_file(file):
            return None
        else:
            file_content = file.data_stream.read().decode('utf-8')
            return file_content
    except KeyError:
        print(f"File {path} not found in commit {commit.hexsha}")
        return None


def read_readme_content(repo):
    head_commit = repo.head.commit

    try:
        readme_content = head_commit.tree / 'README.md'
    except:
        readme_content = head_commit.tree / 'README'

    try:
        return readme_content.data_stream.read().decode('utf-8')
    except KeyError:
        logging.log(level=logging.WARN, msg=f"仓库 {repository_url} 不存在README.md文件")
        return ""

def clone_repository(repository_url, destination_folder):
    try:
        repo = git.Repo.clone_from(repository_url, destination_folder)
    except git.exc.GitCommandError:
        logging.log(level=logging.ERROR, msg=f"克隆仓库 {repository_url} 到 {destination_folder} 时产生错误")
        return None
    return repo

def update_repository(destination_folder):
    try:
        repo = git.Repo(destination_folder)
        remote = repo.remote()
        remote.pull()
    except git.exc.GitCommandError:
        logging.log(level=logging.ERROR, msg=f"更新仓库 {destination_folder} 时产生错误")
        return None
    return repo

def load_data(repo, db_repo):
    db_repo.readme = read_readme_content(repo)
    for commit in repo.iter_commits():
        if localtime(db_repo.updated) > commit.committed_datetime:
            break
        _commit = Commit()
        _commit.repository = db_repo
        _commit.content = commit.message
        _commit.hash = commit.hexsha
        _commit.time = commit.authored_datetime
        _commit.save()

        for parent in commit.parents:
            for change in parent.diff(commit):
                file = File()
                file.type = change.change_type
                file.path = change.b_path
                file.commit = _commit
                file.binary = False

                if file.type == "A":
                    file.content_new = binary_filter(commit, change.b_path)
                elif file.type == "D":
                    file.content_old = binary_filter(commit, change.a_path)
                else:
                    file.diff = repo.git.diff("-U 0", parent, commit, "--", change.a_path, change.b_path)
                    file.content_old = binary_filter(parent, change.a_path)
                    file.content_new = binary_filter(commit, change.b_path)
                if file.content_new is None:
                    file.binary = True
                file.save()
    db_repo.updated = timezone.now()


def init(db_repo, repository_url):
    repo = clone_repository(repository_url, CLONE_PREFIX + db_repo.hash)
    if repo is None:
        raise ConnectionError("仓库克隆失败")
    load_data(repo, db_repo)

def update(db_repo):
    repo = update_repository(CLONE_PREFIX + db_repo.hash)
    if repo is None:
        raise ConnectionError("仓库更新失败")
    load_data(repo, db_repo)


if __name__ == '__main__':
    repository_url = "https://github.com/NicerWang/ToyRPC"
    db_repo = Repository()
    init(db_repo, repository_url)
